<template>
  <div class="cesium-main">
    <div ref="container" class="cesium-container" />
    <div class="cesium-ui">
      <template v-if="ready">
        <slot />
      </template>
    </div>
  </div>
</template>

<script>
import { Viewer, ScreenSpaceEventHandler, ScreenSpaceEventType } from "cesium";
import "cesium/Build/Cesium/Widgets/widgets.css";
import { Event } from "@/utils/eventbus";
import kebabCase from "lodash/kebabCase";
import throttle from "lodash/throttle";
export default {
  name: "cesium",
  props: {
    properties: {
      type: Object,
      default: () => {
        return Object.freeze({});
      }
    },
    preConfig: {
      type: Function,
      default: () => {}
    }
  },
  data() {
    return {
      ready: false
    };
  },
  created() {
    this.eventBus = new Event();
  },
  mounted() {
    this.$nextTick(() => {
      this.createViewer();
    });
  },
  beforeDestroy() {
    this.removeViewer();
  },
  methods: {
    createViewer() {
      this.viewer = window.viewer = new Viewer(
        document.querySelector(".cesium-container"),
        this.properties
      );
      // 解决pickPosition拾取错误的问题
      this.viewer.scene.globe.depthTestAgainstTerrain = false;
      this.preConfig(this.viewer);
      this.attachEvents();
      this.ready = true;
      //   缩放最大高度
      //   this.viewer.scene.screenSpaceCameraController.minimumZoomDistance = 300;
    },
    removeViewer() {
      this.detachEvents();
      this?.viewer.destroy();
    },
    attachEvents() {
      this.attachViewerEvents();
      this.attachCameraEvents();
      this.attachSceneEvents();
    },
    detachEvents() {
      if (this.eventBus) {
        this.eventBus?.destroy();
      }
      this?.detachViewerEvents();
      this?.detachCameraEvents();
      this?.detachSceneEvents();
    },
    attachViewerEvents() {
      this.eventBus.entitys = [];
      if (this.handler) this.detachEvents();
      const handler = (this.handler = new ScreenSpaceEventHandler(
        this.$refs.container
      ));
      const that = this;
      for (const key in ScreenSpaceEventType) {
        const value = ScreenSpaceEventType[key];
        handler.setInputAction(
          throttle(function(e) {
            that.handleEvent(e, kebabCase(key), value);
          }, 30),
          value
        );
      }
    },
    detachViewerEvents() {
      if (this.handler) {
        this.handler?.destroy();
      }
    },
    attachCameraEvents() {
      const { camera } = this.viewer.scene;
      const eventList = ["moveStart", "moveEnd", "changed"];
      const removeList = [];
      const that = this;
      eventList.forEach(event => {
        removeList.push(
          camera[event].addEventListener(
            throttle(function() {
              that.handleCameraEvent(`camera-${kebabCase(event)}`);
            }, 30)
          )
        );
      });
      this.detachCameraEvents = () => {
        removeList.forEach(i => i());
      };
    },
    attachSceneEvents() {
      const { scene } = this.viewer;
      const eventList = [
        "morphComplete",
        "morphStart",
        "postRender",
        "postUpdate",
        "preRender",
        "preUpdate",
        "terrainProviderChanged"
      ];
      const removeList = [];
      eventList.forEach(event => {
        removeList.push(
          scene[event].addEventListener(
            throttle(() => {
              this.handleSceneEvent(`scene-${kebabCase(event)}`);
            }, 30)
          )
        );
      });
      this.detachSceneEvents = () => {
        removeList.forEach(i => i());
      };
    },
    handleEvent(e, eventName) {
      const mousePosition = e.position || e.endPosition;
      let position = this.pickPosition(mousePosition);
      let picked = this.pickObj(mousePosition, true);
      this.$emit(eventName, {
        ...e,
        picked,
        pickedPosition: position
      });
      picked?.forEach(i => {
        const emits = [];

        if (i.id && i.id.$emit) emits.push(i.id.$emit);
        if (i.$emit) emits.push(i.$emit);
        emits.forEach(emit => {
          emit(eventName, {
            ...e,
            picked
          });
        });
      });
    },
    handleCameraEvent(eventName) {
      const { camera } = this.viewer.scene;
      this?.eventBus?.emit(eventName, {
        eventType: eventName,
        event: camera
      });
      this.$emit(eventName, camera);
    },
    handleSceneEvent(eventName) {
      const scene = this.viewer.scene;
      this?.eventBus?.emit(eventName, {
        eventType: eventName,
        event: scene
      });
      this.$emit(eventName, scene);
    },
    removeEntity(entity) {
      try {
        this?.viewer.entities.remove(entity);
      } catch (error) {}
    },
    pickPosition(screenPosition) {
      return this?.viewer?.scene?.pickPosition(screenPosition);
    },
    pickObj(screenPosition, drill = false) {
      try {
        return drill
          ? this.viewer.scene.drillPick(screenPosition)
          : this.viewer.scene.pick(screenPosition);
      } catch (error) {}
      return undefined;
    }
  }
};
</script>

<style lang="scss" scoped>
.cesium-main {
  position: relative;
  &::v-deep {
    .cesium-viewer-bottom,
    .cesium-viewer-animationContainer {
      display: none;
    }
  }
}
.cesium-main,
.cesium-container,
.cesium-ui {
  width: 100%;
  height: 100%;
  overflow: hidden;
}
.cesium-container {
  position: absolute;
  z-index: 1;
}
.cesium-ui {
  //   z-index: 2;
  position: absolute;
  top: 0px;
  left: 0px;
  pointer-events: none;
  & * {
    pointer-events: all;
  }
}
</style>
